﻿Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Net.Mail
Imports System.Net
Imports System.IO

Namespace Seuss.EnterpriseLibrary.Mail

    Public Class WebpageMailer

        Private _mailMessage As New MailMessage()

        Public ReadOnly Property MailMessage() As MailMessage
            Get
                Return _mailMessage
            End Get
        End Property

        Private _smptServerName As String

        Public Property SmtpServerName() As String
            Get
                Return _smptServerName
            End Get
            Set(ByVal value As String)
                _smptServerName = value
            End Set
        End Property

        Private _delay As Integer

        Public Property Delay() As Integer
            Get
                Return _delay
            End Get
            Set(ByVal value As Integer)
                _delay = value
            End Set
        End Property

        Public Sub SendMailMessage(ByVal uri As Uri)

            Dim htmlbody As String = GetBody(uri)

            ' Que imagenes hay para embeber
            Dim modifiedbody As String = String.Empty
            Dim foundResources As List(Of LinkedResource)
            ExtractLinkedResources(uri, htmlbody, modifiedbody, foundResources)

            ' Escribe el html al flujo de memoria
            Dim stream As New MemoryStream()
            Dim bytes As Byte() = System.Text.Encoding.ASCII.GetBytes(modifiedbody)
            stream.Write(bytes, 0, bytes.Length)
            stream.Position = 0

            ' Configura el correo para contener la pagina web
            _mailMessage.Body = "Este es un email html - utiliza un cliente de correo que pueda leerlo."
            Dim altView As New AlternateView(stream, System.Net.Mime.MediaTypeNames.Text.Html)

            ' Embebe las imagenes dentro del email
            For Each linkedResource As LinkedResource In foundResources
                altView.LinkedResources.Add(linkedResource)
            Next
            _mailMessage.AlternateViews.Add(altView)

            _mailMessage.From = New System.Net.Mail.MailAddress("areyes@ucv.edu.pe")
            _mailMessage.To.Add("areyes@ucv.edu.pe")

            ' Envia el email
            Dim client As New SmtpClient(_smptServerName)
            client.Send(_mailMessage)
        End Sub

        Public Shared Function GetBody(ByVal uri As Uri) As String
            Dim request As HttpWebRequest = DirectCast(WebRequest.Create(uri), HttpWebRequest)

            ' Set some reasonable limits on resources used by this request
            request.MaximumAutomaticRedirections = 4
            request.MaximumResponseHeadersLength = 4
            ' Set credentials to use for this request.
            request.Credentials = CredentialCache.DefaultCredentials
            Dim response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)

            ' Get the stream associated with the response.
            Dim receiveStream As Stream = response.GetResponseStream()

            ' Pipes the stream to a higher level stream reader with the required encoding format. 
            Dim readStream As New StreamReader(receiveStream, Encoding.UTF8)

            Dim body As String = readStream.ReadToEnd()
            readStream.Close()

            response.Close()
            Return body
        End Function

        Private Function GetImageStream(ByVal uri As Uri, ByRef contentType As String) As Stream
            System.Threading.Thread.Sleep(_delay)

            Dim request As HttpWebRequest = DirectCast(WebRequest.Create(uri), HttpWebRequest)

            ' Set some reasonable limits on resources used by this request
            request.MaximumAutomaticRedirections = 4
            request.MaximumResponseHeadersLength = 4
            ' Set credentials to use for this request.
            request.Credentials = CredentialCache.DefaultCredentials
            Dim response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)

            ' Get the stream associated with the response.
            Dim receiveStream As Stream = response.GetResponseStream()

            Dim binaryReader As New BinaryReader(receiveStream)
            Dim memoryStream As New MemoryStream()
            Try
                While True
                    Dim b As Byte = binaryReader.ReadByte()
                    ' Not so efficient but it works...
                    memoryStream.WriteByte(b)
                End While
            Catch generatedExceptionName As EndOfStreamException
                memoryStream.Position = 0
            End Try

            contentType = response.ContentType
            response.Close()
            Return memoryStream
        End Function

        Private Sub ExtractLinkedResources(ByVal uri As Uri, ByVal html As String, ByRef modifiedhtml As String, ByRef linkedResources As List(Of LinkedResource))
            modifiedhtml = html
            linkedResources = New List(Of LinkedResource)()

            Dim imageNames As List(Of String) = ExtractImageNames(html)

            Dim imageID As Integer = 1

            For Each imageName As String In imageNames
                Try
                    ' Deal with some escape characters that can occur in dynamic image url's
                    Dim workaroundImageName As String = imageName.Replace("&amp;", "&")

                    ' Generate the uri to retrieve the image - usually an image path is relative to the page uri
                    Dim imageUri As New Uri(uri, workaroundImageName)

                    ' Retrieve the image
                    Dim contentType As String = String.Empty
                    Dim imageStream As Stream = GetImageStream(imageUri, contentType)

                    ' Fill the linked resource
                    Dim data As New LinkedResource(imageStream)

                    ' Determine a name and set the media type of the linked resource
                    Dim generatedName As String = Nothing
                    If contentType.ToLower().IndexOf("image/gif") >= 0 Then
                        data.ContentType.MediaType = System.Net.Mime.MediaTypeNames.Image.Gif
                        generatedName = "image" & imageID.ToString() & ".gif"
                    ElseIf contentType.ToLower().IndexOf("image/jpeg") >= 0 Then
                        data.ContentType.MediaType = System.Net.Mime.MediaTypeNames.Image.Jpeg
                        generatedName = "image" & imageID.ToString() & ".jpeg"
                    End If

                    ' it is something that I don't handle yet
                    If generatedName Is Nothing Then
                        Continue For
                    End If

                    ' Generate the linked resource for the image being embedded
                    Dim generatedSrc As String = "cid:" & generatedName
                    data.ContentType.Name = generatedName
                    data.ContentId = generatedName
                    data.ContentLink = New Uri(generatedSrc)
                    linkedResources.Add(data)

                    ' Let the html refer to the linked resource
                    modifiedhtml = modifiedhtml.Replace(imageName, generatedSrc)
                Catch
                    modifiedhtml = modifiedhtml.Replace(imageName, "#")
                End Try

                imageID += 1
            Next
        End Sub

        Private Class LengthComparer
            Implements IComparer(Of String)

            Public Function Compare(ByVal x As String, ByVal y As String) As Integer
                Return -x.Length.CompareTo(y.Length)
            End Function

            Public Function Compare1(ByVal x As String, ByVal y As String) As Integer Implements System.Collections.Generic.IComparer(Of String).Compare

            End Function
        End Class

        Private Shared Function SortNoDuplicate(ByVal input As List(Of String)) As List(Of String)
            Dim result As New List(Of String)(input)
            Dim comparer As IComparer(Of String) = New LengthComparer()
            result.Sort(comparer)
            Return result
        End Function

        Private Shared Function ExtractImageNames(ByVal html As String) As List(Of String)
            Dim imagenames As New List(Of String)()
            Dim imageattributes As String() = New String() {"src=", "background="}
            For Each imageattribute As String In imageattributes
                Dim position As Integer = 0
                While position < html.Length
                    Dim foundIndex As Integer = html.ToLower().IndexOf(imageattribute, position)
                    If foundIndex < 0 Then
                        position = html.Length
                    Else
                        Dim valueStartIndex As Integer = foundIndex + imageattribute.Length + 1
                        Dim foundIndexEnd As Integer = html.IndexOfAny(New Char() {""""c, " "c, "'"c, ">"c}, valueStartIndex)
                        If foundIndexEnd < 0 Then
                            position = html.Length
                        Else
                            Dim relativeimagename As String = html.Substring(valueStartIndex, foundIndexEnd - valueStartIndex)
                            relativeimagename = relativeimagename.Trim(New Char() {""""c, " "c, "'"c, ">"c})
                            If Not imagenames.Contains(relativeimagename) Then
                                imagenames.Add(relativeimagename)
                            End If
                            position = foundIndexEnd
                        End If
                    End If
                End While
            Next
            Return SortNoDuplicate(imagenames)
        End Function

    End Class

End Namespace
